package com.googlecode.restitory.gae.hook;

import java.util.ArrayList;
import java.util.List;


import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.googlecode.mycontainer.commons.json.JsonHandler;
import com.googlecode.restitory.gae.Direction;
import com.googlecode.restitory.gae.GAERequestUtil;
import com.googlecode.restitory.gae.Operator;
import com.googlecode.restitory.gae.Request;
import com.googlecode.restitory.gae.Response;

public class QueryHook implements Hook {

	@Override
	public Response execute(Request request, Chain chain) {
		String path = request.getPath();
		if (path.equals("/_/query")) {
			return query(request);
		}
		return chain.execute(request);
	}

	public DatastoreService getDatastoreService() {
		return DatastoreServiceFactory.getDatastoreService();
	}

	private Response query(Request req) {
		Query query = new Query("resource");
		query.setKeysOnly();
		List<String> filters = req.getParams().get("f");
		for (String filter : filters) {
			addFilter(query, filter);
		}
		List<String> orders = req.getParams().get("s");
		for (String order : orders) {
			addOrder(query, order);
		}
		Integer offset = req.getParams().getAsInteger("o");
		Integer limit = req.getParams().getAsInteger("l");
		PreparedQuery prepared = getDatastoreService().prepare(query);
		List<Entity> list = prepared.asList(GAERequestUtil.getFetch(offset,
				limit));
		List<String> ret = new ArrayList<String>(list.size());
		for (Entity entity : list) {
			ret.add(entity.getKey().getName());
		}
		String json = JsonHandler.instance().format(ret);
		return new Response(req).setCode(200)
				.setContentType("application/json").setContent(json);
	}

	private void addOrder(Query query, String order) {
		String[] split = order.split(":");
		Direction dir = Direction.valueOf(split[1]);
		SortDirection sd = GAERequestUtil.directionToSort(dir);
		query.addSort(split[0], sd);
	}

	private void addFilter(Query query, String filter) {
		String[] split = filter.split(":");
		Operator op = Operator.valueOf(split[1]);
		FilterOperator filterOperator = GAERequestUtil.operatorToFilter(op);

		JsonParser parser = new JsonParser();
		JsonElement x = parser.parse(split[2]);
		Object value = null;
		if (!x.isJsonNull()) {
			JsonPrimitive parsed = (JsonPrimitive) x;
			if (parsed.isBoolean()) {
				value = parsed.getAsBoolean();
			} else if (parsed.isNumber()) {
				Number n = parsed.getAsNumber();
				if (1.0 * n.longValue() != n.doubleValue()) {
					value = n.doubleValue();
				} else {
					value = n.longValue();
				}
			} else {
				value = parsed.getAsString();
			}
		}
		query.addFilter(split[0], filterOperator, value);
	}

}
